#include "anillo.h"
#include "aed2_tests.h"

template<typename T>
string to_s(const Anillo<T>* a) {
	ostringstream os;
	os << *a;
	return os.str();
}

void AnilloNuevoEsVacio() {
	Anillo<int>* a = new Anillo<int>();
	ASSERT(a->esVacio());
	ASSERT_EQ(a->tamanio(), 0);
	delete a;
}

void AnilloUnitarioAlEliminarQuedaVacio() {
	Anillo<int>* a = new Anillo<int>();
	ASSERT(a->esVacio());
	ASSERT_EQ(a->tamanio(), 0);
    a->agregar(42);
	ASSERT(!a->esVacio());
	ASSERT_EQ(a->tamanio(), 1);
    a->eliminar(42);
	ASSERT(a->esVacio());
	ASSERT_EQ(a->tamanio(), 0);
	delete a;
}

void AnilloUnitarioDaSiguiente() {
	Anillo<int>* a = new Anillo<int>();
    a->agregar(42);
    ASSERT(!a->esVacio());
    ASSERT_EQ(a->tamanio(), 1);
    ASSERT_EQ(a->siguiente(), 42);
	delete a;
}

void MostrarAnilloVacio() {
	Anillo<int>* a = new Anillo<int>();
	ASSERT_EQ(to_s(a), "[]");
	delete a;
}

void Tamanio() {
	Anillo<int>* a = new Anillo<int>();
	ASSERT_EQ(a->tamanio(), 0);
	
	a->agregar(10);
	ASSERT_EQ(a->tamanio(), 1);
	
	a->agregar(20);
	ASSERT_EQ(a->tamanio(), 2);
	
	a->agregar(30);
	ASSERT_EQ(a->tamanio(), 3);
	
	// Estas operaciones no deberían modificar el tamaño
	a->marcar();
	a->siguiente();
	a->retroceder();
	
	ASSERT_EQ(a->tamanio(), 3);
	
	a->eliminar(30);
	ASSERT_EQ(a->tamanio(), 2);
	
	a->eliminar(10);
	ASSERT_EQ(a->tamanio(), 1);
	
	a->eliminar(20);
	ASSERT_EQ(a->tamanio(), 0);
	
	a->agregar(10);
	ASSERT_EQ(a->tamanio(), 1);
	
	delete a;
}

void MostrarAnillo() {
	Anillo<int>* a = new Anillo<int>();
	ASSERT_EQ(to_s(a), "[]");
	
	a->agregar(10);
	ASSERT_EQ(to_s(a), "[10]");
	
	a->agregar(20);
	ASSERT_EQ(to_s(a), "[20, 10]");
	
	a->agregar(30);
	ASSERT_EQ(to_s(a), "[30, 20, 10]");
	
	a->marcar();
	ASSERT_EQ(to_s(a), "[30*, 20, 10]");
	
	a->siguiente();
	ASSERT_EQ(to_s(a), "[20, 10, 30*]");
	
	a->siguiente();
	ASSERT_EQ(to_s(a), "[10, 30*, 20]");
	
	delete a;
}

void Marcar() {
	Anillo<int>* a = new Anillo<int>();
	a->agregar(10);
	a->agregar(20);
	a->agregar(30);
	a->agregar(40);
	a->agregar(50);
	
	ASSERT(!a->hayMarcado());
	
	a->marcar();
	
	ASSERT(a->hayMarcado());
	ASSERT_EQ(a->marcado(), 50);
	
	a->siguiente();
	
	ASSERT_EQ(a->actual(), 40);
	ASSERT_EQ(a->marcado(), 50);
	
	a->eliminar(50);
	
	ASSERT(!a->hayMarcado());
	
	a->siguiente();
	a->marcar();
	
	ASSERT(a->hayMarcado());
	
	a->siguiente();
	
	ASSERT(a->hayMarcado());
	ASSERT_EQ(a->marcado(), 30);
	
	delete a;
}

void AgregarYEliminar() {
	Anillo<int> *a = new Anillo<int>();
	
	a->agregar( 10 );
	a->agregar( 20 );
	a->agregar( 30 );
	a->agregar( 40 );
	a->agregar( 50 );
	a->agregar( 60 );
	a->agregar( 70 );
	
	// Verificamos que se hayan agrgado todos bien
	ASSERT_EQ(a->siguiente(), 70);
	ASSERT_EQ(a->siguiente(), 60);
	ASSERT_EQ(a->siguiente(), 50);
	ASSERT_EQ(a->siguiente(), 40);
	ASSERT_EQ(a->siguiente(), 30);
	ASSERT_EQ(a->siguiente(), 20);
	ASSERT_EQ(a->siguiente(), 10);
	
	// Eliminamos algunos elementos
	a->eliminar( 60 );
	a->eliminar( 40 );
	a->eliminar( 10 );
	
	// Verificamos que se hayan eliminado así como que se siga manteniendo
	// el orden
	ASSERT_EQ(a->siguiente(), 70);
	ASSERT_EQ(a->siguiente(), 50);
	ASSERT_EQ(a->siguiente(), 30);
	ASSERT_EQ(a->siguiente(), 20);
	
	// Si eliminamos el actual
	ASSERT_EQ(a->actual(), 70);
	
	a->eliminar(70);
	
	ASSERT_EQ(a->actual(), 50);
	
	// Si eliminamos un elemento marcado
	a->marcar();
	
	ASSERT(a->hayMarcado());
	ASSERT_EQ(a->marcado(), 50);
	
	a->siguiente();
	a->eliminar(50);
	
	ASSERT(!a->hayMarcado());
	
	delete a;
}

void Copia() {
	Anillo<int> a;
	a.agregar( 23 );
	a.agregar( 48 );
	a.agregar( 57 );
	a.siguiente();
	a.marcar();

	Anillo<int> b( a );

	ASSERT_EQ( a.actual(), b.actual() );
	ASSERT_EQ( a.tamanio(), b.tamanio() );
	ASSERT_EQ( a.hayMarcado(), b.hayMarcado() );
	ASSERT_EQ( a.marcado(), b.marcado() );
	ASSERT_EQ( a.siguiente(), b.siguiente() );
	ASSERT_EQ( a.siguiente(), b.siguiente() );
	ASSERT_EQ( a.siguiente(), b.siguiente() );
	ASSERT_EQ( a.siguiente(), b.siguiente() );
	ASSERT_EQ( a.siguiente(), b.siguiente() );

	Anillo<int> c;
	c.agregar( 24 );
	c.marcar();
	Anillo<int> d( c );

	ASSERT_EQ( c.actual(), d.actual() );
	ASSERT_EQ( c.tamanio(), d.tamanio() );
	ASSERT_EQ( c.hayMarcado(), d.hayMarcado() );
	ASSERT_EQ( c.marcado(), d.marcado() );
	ASSERT_EQ( c.siguiente(), d.siguiente() );
	ASSERT_EQ( c.siguiente(), d.siguiente() );
	ASSERT_EQ( c.siguiente(), d.siguiente() );


	Anillo<int> e;
	Anillo<int> f( e );

	ASSERT_EQ( e.tamanio(), f.tamanio() );
	ASSERT_EQ( e.hayMarcado(), f.hayMarcado() );
}

void Igualdad() {
	Anillo<int> a, b;
	ASSERT( a == b );

	a.agregar( 24 ); b.agregar( 24 ); ASSERT( a == b );
	a.agregar( 23 ); b.agregar( 23 ); ASSERT( a == b );
	a.agregar( 28 ); b.agregar( 28 ); ASSERT( a == b );

	a.siguiente();
	ASSERT( !(a == b) );

	b.siguiente();
	ASSERT( a == b );

	a.marcar();
	ASSERT( !(a == b) );

	b.marcar();
	ASSERT( a == b );

	a.retroceder();
	ASSERT( !(a == b) );
	b.retroceder();
	ASSERT( a == b );
}

void PruebasVarias() {
	Anillo<int>* a = new Anillo<int>();
	
	a->agregar(10);
	ASSERT_EQ(a->actual(), 10);
	ASSERT_EQ(a->tamanio(), 1);
	
	a->agregar(20);
	ASSERT_EQ(a->actual(), 20);
	ASSERT_EQ(a->tamanio(), 2);
	
	a->agregar(30);
	ASSERT_EQ(a->actual(), 30);
	ASSERT_EQ(a->tamanio(), 3);
	
	ASSERT(!a->esVacio());
	
	ASSERT_EQ(a->siguiente(), 30);
	ASSERT_EQ(a->actual(), 20);
	
	ASSERT_EQ(a->siguiente(), 20);
	ASSERT_EQ(a->actual(), 10);
	
	ASSERT_EQ(a->siguiente(), 10);
	ASSERT_EQ(a->actual(), 30);
	
	a->eliminar(20);
	ASSERT_EQ(a->tamanio(), 2);
	
	a->eliminar(30);
	ASSERT_EQ(a->tamanio(), 1);
	
	ASSERT_EQ(a->actual(), 10);
	ASSERT_EQ(a->siguiente(), 10);
	ASSERT_EQ(a->actual(), 10);
	
	a->eliminar(10);
	
	ASSERT(a->esVacio());
	ASSERT_EQ(a->tamanio(), 0);
	
	a->agregar(40);
	ASSERT_EQ(a->actual(), 40);
	ASSERT_EQ(a->tamanio(), 1);
	
	a->agregar(50);
	ASSERT_EQ(a->actual(), 50);
	ASSERT_EQ(a->tamanio(), 2);
	
	a->agregar(60);
	ASSERT_EQ(a->actual(), 60);
	ASSERT_EQ(a->tamanio(), 3);
	
	ASSERT(!a->esVacio());
	
	a->siguiente();
	
	ASSERT_EQ(a->actual(), 50);
	
	a->marcar();
	
	ASSERT(a->hayMarcado());
	ASSERT_EQ(a->marcado(), 50);
	
	a->siguiente();
	
	ASSERT_EQ(a->actual(), 40);
	ASSERT_EQ(a->marcado(), 50);
	
	a->eliminar(50);
	
	ASSERT(!a->hayMarcado());
	
	a->siguiente();
	ASSERT_EQ(a->actual(), 60);
	a->siguiente();
	ASSERT_EQ(a->actual(), 40);
	
	delete a;
}

int main(void) {
	RUN_TEST(AnilloNuevoEsVacio);
	RUN_TEST(AnilloUnitarioAlEliminarQuedaVacio);
	RUN_TEST(AnilloUnitarioDaSiguiente);
	RUN_TEST(MostrarAnilloVacio);
	RUN_TEST(MostrarAnillo);
	RUN_TEST(Tamanio);
	RUN_TEST(Marcar);
	RUN_TEST(AgregarYEliminar);
	RUN_TEST(Copia);
	RUN_TEST(Igualdad);
	RUN_TEST(PruebasVarias);

	return 0;
}

